「出張! #DevelopersIO IT技術ブログの中の人が語る勉強会」でLambdaとEventBridge Pipesを例にSQSコンシューマーの疎結合進化について語りました
弊社技術ブログ「DevelopersIO」の書いた本人が過去記事を深掘って話すという勉強会の第3回が2024/04/18に開催されました。
今回は、2006から本稼働しているメッセージキューサービスのAmazon SQSのコンシューマー処理が2014年のAWS Lambda、2022年のAmazon EventBridge Pipesの登場とともに、疎結合に進化していったことについて、re:Invent 2022の EventBridge Pipes発表直後に書いた次のブログをベースに深堀りました。
EventBridge Pipesは非同期のメッセージ通信で幅広く使えるのに、今ひとつ認知・採用されていないなぁという思いと、相性が良いSQS-Lambda連携について、現在はEventBridge Pipesを挟む選択肢もあることを知ってもらいたくて、このテーマを取り上げました。
勉強会で使用した資料
メッセージキューとは?
まずはメッセージキューについておさらいしましょう。
メッセージキューは、プロデューサーとコンシューマーが非同期に通信するメッセージングパターンです。
マイクロサービスやイベント駆動アーキテクチャーなどでは疎結合であることが求められます。 疎結合を実現する中心的なAWSサービスの一つが、メッセージキューのAmazon SQSです。
Amazon SQSコンシューマー疎結合への旅
メッセージキューの重要性から、Amazon SQSは15年以上前から存在し、時代によってコンシューマー処理の選択肢も当然変わってきました。
今回発表ではAWSの進化によってコンシューマー処理が疎結合になってきたことについて
- モノリス
- Lambda
- EventBridge Pipes
という3つの変遷について紹介しましす。
モノリス時代
まずはモノリス時代です。
この時代では、アプリケーションがメッセージキューのすべての操作を担います。 具体的には、SQSキューをポーリングしてメッセージを受信し、処理してキューから削除するという一連の操作です。
また、メッセージ数に応じてスケーリングするような実行基盤を用意する必要もあります。
コンシューマーはモノリシックで非常に重厚な作りです。
import boto3 sqs = boto3.client('sqs') SQS_QUEUE_URL = 'https://sqs.ap-northeast-1.amazonaws.com/123456789012/bar' response = sqs.receive_message( QueueUrl = SQS_QUEUE_URL, ) for message in response.get("Messages", []): # メッセージの受信 print(message["Body"]) # 処理 sqs.delete_message( QueueUrl = SQS_QUEUE_URL, ReceiptHandle = message['ReceiptHandle'] ) # メッセージの削除
コンシューマーはメッセージ処理(ビジネスロジック)に注力したいのに
- SQSをポーリング
- メッセージを受信
- メッセージを処理
- メッセージをSQSから削除
といったSQSそのものの処理も必要です。
更には、SQSキューのメッセージ数に応じて、コンシューマーをAutoScaling等でスケールさせる必要もあります。
Lambda時代
2014年にLambdaが発表されて新しい時代がやってきました。
LambdaのトリガーにSQSを指定するだけで、SQSの操作をLambdaのイベントソースにオフロードできるようになり、メッセージ数に応じてLambdaもスケールします。
モノリス時代はアプリケーションにいろいろな処理が含まれていましたが、Lambda時代はSQSのことは忘れ、受け取ったメッセージの処理だけに注力できるようになり、コードが非常にシンプルになりました
def lambda_handler(event, context): for message in event['Records']: print(message['body'])
この3行だけで、実際に動作します。AWS SDKのインポート(import boto3
)は不要です
ただし、LambdaイベントソースがSQSを裏で操作しているのでLambdaの実行ロールにはSQSのアクションも許可します。
コードと権限が一致しない点には注意が必要です。
ちなみに、クラスメソッドに入社して最初期に対応した案件の一つが、シェルスクリプトで書かれたKinesisコンシューマーのLambda化でした。
Amazon Kinesis Data Streamsはシャーディングでスケールアウトし、シャード単位でメッセージに順序がついているため、コンシューマーはシャード単位でのチェックポイント管理が求められます。
Lambdaの登場後、ストリーム処理に慣れ親しんだ超人でないととっつきにくかったKinesisが一気に身近になったのを思い出します。
EventBridge Pipesの登場
2022年に発表されたAmazon EventBridge Pipesとともに新しい時代がやってきます。
Pipesはコンシューマー処理をソース・ポーリング・ターゲットに3分割し、Lambdaの一機能だったSQS操作はPipesが担います。
def lambda_handler(event, context): for message in event: print(message['body'])
実装コードはLambdaイベントソース時代と同等ですが、Lambdaの実行ロールにSQSアクションは不要になりました。 代わりに、Pipesの実行ロールでSQSのメッセージ操作とLambda呼び出しを許可します。
EventBridge Pipesを使うことで、誰が何を行うのか明確になり、疎結合性が向上しました。
- モノリス
- Lambda
- EventBridge Pipes
と時代が進むにつれ、メッセージキューSQSのコンシューマー処理が疎結合に進化していったと感じられたでしょうか?
参考
- 出張! #DevelopersIO IT技術ブログの中の人が語る勉強会 #3 - connpass
- Amazon EventBridge Pipesを使ってプロデューサー/コンシューマー型メッセージ処理のパイプラインを簡略化しよう #reinvent | DevelopersIO
- Amazon SQSワーカーのアーキテクチャーをLambdaイベントソース/EventBridge Pipes/独自の3パターンで比較してみた | DevelopersIO
- Amazon SQSコンシューマー疎結合への旅 - 出張! #DevelopersIO IT技術ブログの中の人が語る勉強会 #3 - Speaker Deck